package thread;

import form.GUI;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.util.Pair;
import services.Command;


import services.Command.ClientType;
import services.Command.Include;
import services.Command.MessageType;
import services.Host;
import services.Packet;
import services.ResearchFile;
import side.JacksonStream;
import thread.transferfileClient.*;

public class DiscoveryThreadClient implements Runnable {

    DatagramSocket c;
    String commandEcho;
    int msgPort = 4444;
    int dataPort = 4445;
    public static boolean first = true;
    boolean is = false;
    InetAddress ownIP;
   
    @Override
    public void run() {

        try {

            c = new DatagramSocket();
            c.setBroadcast(true);
            c.setSoTimeout(7000);


            while (true) {

                if (c.isClosed()) {
                    c = new DatagramSocket();
                    c.setBroadcast(true);
                    c.setSoTimeout(7000);
                }

                //    if (GUI.counter % 2 == 0 || first) {
                if (first || GUI.counter % 30 == 0) {
                    System.out.println(": " + String.valueOf(GUI.counter));
                    byte[] sData = Packet.generatePacketAsString(ClientType.CLIENT, MessageType.REQUEST, Include.INFO, "HELLO").getBytes();

                    //Try the 255.255.255.255 first
                    try {


                        DatagramPacket sendPacket = new DatagramPacket(sData, sData.length, InetAddress.getByName("255.255.255.255"), msgPort);
                        c.send(sendPacket);
                        //          System.out.println(getClass().getSimpleName() + ">>> Request packet sent to: 255.255.255.255 (DEFAULT)");
                        //           System.out.println(getClass().getSimpleName() + "::>" + new String(sData));

                        byte[] recvBuf = new byte[15000];
                        DatagramPacket receivePacket = new DatagramPacket(recvBuf, recvBuf.length);
                        c.receive(receivePacket);
                        System.out.println(getClass().getSimpleName() + " NOINFO od :" + receivePacket.getAddress().getHostAddress() + "  " + new String(recvBuf) + "\n");
                        for (Host h : Command.clientList) {
                            if (h.getIp().contains(receivePacket.getAddress().getHostAddress().trim())) {
                                is = true;
                                System.out.append(getClass().getSimpleName() + "::> Posiadamy hosta w sieci: " + h.getIp() + "\n");
                            } else {
                                is = false;
                            }
                        }
                        String st = new String(recvBuf);
                        if (st.contains("SERVER;RESPONSE;INFO;NOINFO")) {

                            Pair p = Packet.splitPacket(recvBuf, receivePacket.getAddress().getAddress().toString());
                            String a = (String) p.getKey();
                            ArrayList<String> b = (ArrayList<String>) p.getValue();
                            for (String s : b) {
                                System.out.println(getClass().getName() + " :" + s + "\n");
                            }

                            try {

                                GUI.listingTree();
                                for (ResearchFile r : Command.def.getFile()) {
                                    System.out.append(getClass().getSimpleName() + ":>> " + r.getName() + "\n");
                                }

                                JacksonStream.createJsonFile(Command.def.getIp(), Command.def.getFile());
                                side.SideDish.wait(5000);
                                System.out.append(getClass().getSimpleName() + "Utworzono ponownie plik: " + InetAddress.getLocalHost().getHostAddress() + ".json\n");
                                GUI.lf.listFile(".json");


                                System.out.append(getClass().getSimpleName() + "::> Create file: " + InetAddress.getLocalHost().getHostAddress() + ".json\n");


                                runFTPClient(receivePacket.getAddress().getHostAddress(), dataPort, Command.Action.PUT, InetAddress.getLocalHost().getHostAddress() + ".json");
                                System.out.append(getClass().getSimpleName() + "Umieszczono plik na hoście o adresie: " + receivePacket.getAddress().getHostAddress() + ".json\n");

                                side.SideDish.wait(200);
                                runFTPClient(receivePacket.getAddress().getHostAddress(), dataPort, Command.Action.GET, receivePacket.getAddress().getHostAddress().toString() + ".json");
                                System.out.append(getClass().getSimpleName() + "::> Odebrano plik od hosta o adresie: " + receivePacket.getAddress().getHostAddress() + ".json\n");
                                //        Command.clientList.add(new Host(receivePacket.getAddress().getHostName(), receivePacket.getAddress().getHostAddress()));
                                GUI.lf.listFile(".json");

                                for (Host h : Command.clientList) {
                                    System.out.append(getClass().getSimpleName() + "::> Hosty w sieci to: " + h.getName() + "\n");
                                }
                            } catch (Exception ex) {
                                Logger.getLogger(DiscoveryThreadClient.class.getName()).log(Level.SEVERE, null, ex);
                            }




                        }
                    } catch (Exception e) {
                    }


                }
                if (GUI.copyFlag) {
                    for (Host h : Command.clientList) {
                        if (h.getPair() != null) {
                            ArrayList<Pair<CheckBoxTreeItem<String>, CheckBoxTreeItem<String>>> p = (ArrayList<Pair<CheckBoxTreeItem<String>, CheckBoxTreeItem<String>>>) h.getPair();
                            int s = p.size();

                            ArrayList<String> path = new ArrayList<String>();
                            for (int i = 0; i < s; i++) {
                                Pair<CheckBoxTreeItem<String>, CheckBoxTreeItem<String>> pa =
                                        (Pair<CheckBoxTreeItem<String>, CheckBoxTreeItem<String>>) p.get(i);
                                for (ResearchFile r : h.getFile()) {
                                    if (pa.getKey().getValue().equals(r.getName().trim())) {
                                        System.out.println(getClass().getSimpleName() + " Dodano ścieżkę :" + r.getAbsolutePath() + "dla pliku zaznaczonego " + r.getName());
                                        String msg = Packet.generatePacketAsString(ClientType.CLIENT, MessageType.REQUEST, Include.FILE, Command.ResponseCode.DATA.name());
                                        byte[] sendData = msg.getBytes();
                                        System.out.println(getClass().getSimpleName() + "::> Wysłano wiadomość o żądaniu pliku :" + msg);
                                        //Send a response
                                        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName(h.getIp()), msgPort);
                                        c.send(sendPacket);


                                        byte[] rBuf = new byte[15000];
                                        DatagramPacket rPacket = new DatagramPacket(rBuf, rBuf.length);
                                        c.receive(rPacket);
                                        Pair pack = Packet.splitPacket(rBuf, rPacket.getAddress().getAddress().toString());
                                        String a = (String) pack.getKey();
                                        ArrayList<String> b = (ArrayList<String>) pack.getValue();
                                        if (b.get(3).contains(Command.ResponseCode.OK.name())) {
                                            System.out.append("JEST OK\n");
                                            try {
                                                runFTPClient(rPacket.getAddress().getHostAddress(), dataPort, Command.Action.GET, r.getAbsolutePath());
                                            } catch (Exception ex) {
                                                Logger.getLogger(DiscoveryThreadClient.class.getName()).log(Level.SEVERE, null, ex);
                                            }
                                        }
                                        path.add(r.getAbsolutePath());
                                    }
                                }
                            }
                        }
                        h.getPair().clear();

                    }

                //    GUI.lf.listFile(".json");
                    GUI.copyFlag = false;
               //     GUI.refreshFlag = true;
                }
                if (GUI.sendFlag) {
                    for (Host h : Command.clientList) {

                        if (h.getName().equals(GUI.tree.getRoot().getValue().toString())) {
                            for (Pair p : GUI.pair) {
                                CheckBoxTreeItem<String> ch = (CheckBoxTreeItem<String>) p.getKey();
                                String s = null;
                                for (ResearchFile r : Command.def.getFile()) {
                                    if (r.getName().equals(ch.getValue())) {
                                        System.out.println("Wybrano plik do przesyłu o nazwie: " + r.getName() + " o ścieżce: " + r.getAbsolutePath());
                                        s = r.getAbsolutePath();
                                    }
                                }
                                String msg = Packet.generatePacketAsString(ClientType.CLIENT, MessageType.RESPONSE, Include.FILE, Command.ResponseCode.DATA.name());
                                byte[] sendData = msg.getBytes();
                                System.out.println(getClass().getSimpleName() + "::> Wysłano wiadomość o wysłaniu pliku pliku :" + msg);
                                //Send a response
                                DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName(h.getIp()), msgPort);
                                c.send(sendPacket);


                                byte[] rBuf = new byte[15000];
                                DatagramPacket rPacket = new DatagramPacket(rBuf, rBuf.length);
                                c.receive(rPacket);
                                String st = new String(rBuf);
                                if (st.contains("SERVER;REQUEST;FILE;OK")) {

                                    try {
                                        runFTPClient(rPacket.getAddress().getHostAddress(), dataPort, Command.Action.PUT, s);
                                    } catch (Exception ex) {
                                        Logger.getLogger(DiscoveryThreadClient.class.getName()).log(Level.SEVERE, null, ex);
                                    }
                                }
                            }
                        }
                    }


                    GUI.sendFlag = false;
                    GUI.listingFlag = true;
                    GUI.refreshFlag = true;

                }
                //Check if the message is correct
                //           String message = new String(receivePacket.getData()).trim();
                if (GUI.MD5Flag) {
                    System.out.println("Zmieniono md5!!!");
                    for (Host h : Command.clientList) {

                        String msg = Packet.generatePacketAsString(ClientType.CLIENT, MessageType.INFO, Include.JSON, Command.ResponseCode.SYNTAX.name());
                        byte[] sendData = msg.getBytes();

                        //Send a response
                        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName(h.getIp()), msgPort);
                        try {
                            if (c.isClosed()) {
                                c = new DatagramSocket();
                                c.setBroadcast(true);
                                c.setSoTimeout(4000);
                            }
                            c.send(sendPacket);

                            if (c.isClosed()) {
                                c = new DatagramSocket();
                                c.setBroadcast(true);
                                c.setSoTimeout(4000);
                            }
                            System.out.println("Klient wysłał: " + msg + "  " + sendPacket.getAddress().getHostAddress());

                            byte[] rBuf = new byte[15000];
                            DatagramPacket rPacket = new DatagramPacket(rBuf, rBuf.length);

                            c.receive(rPacket);

                            Pair p = Packet.splitPacket(rBuf, rPacket.getAddress().getHostAddress());
                            String a = (String) p.getKey();
                            ArrayList<String> b = (ArrayList<String>) p.getValue();
                            System.out.println("Klient otrzymał: " + new String(rBuf) + " od " + rPacket.getAddress().getHostAddress());
                            if (b.get(0).contains(Command.ClientType.SERVER.name())) {
                                if (b.get(1).contains(Command.MessageType.RESPONSE.name())) {
                                    if (b.get(2).contains(Command.Include.JSON.toString())) {
                                        if (b.get(3).contains(Command.ResponseCode.OK.name())) {

                                            JacksonStream.createJsonFile(Command.def.getIp(), Command.def.getFile());
                                            try {
                                                runFTPClient(rPacket.getAddress().getHostAddress(), dataPort, Command.Action.PUT, Command.def.getIp() + ".json");

                                            } catch (Exception ex) {
                                                Logger.getLogger(DiscoveryThreadClient.class.getName()).log(Level.SEVERE, null, ex);
                                            }
                                            System.out.append("Wysłano do " + rPacket.getAddress().getHostAddress() + " :> \n");
                                            GUI.listingFlag = true;
                                        }
                                    }
                                }
                            }

                        } catch (SocketTimeoutException e) {
                            // timeout exception.
                            System.out.println("Timeout reached!!! " + e);
                            c.close();
                        }
                    }
                    GUI.MD5Flag = false;
                }
            }


        } catch (IOException ex) {
            Logger.getLogger(DiscoveryThreadClient.class
                    .getName()).log(Level.SEVERE, null, ex);
        }


    }

    public void runFTPClient(String hostAddress, int port, Command.Action ac, String filename) throws Exception {
        Socket soc = new Socket(hostAddress, port);
        //  soc.setSoTimeout(5000);
        transferfileClient t = new transferfileClient(soc, ac, filename);
        t.displayMenu();
        return;


    }

    public static DiscoveryThreadClient getInstance() {
        return DiscoveryThreadHolder.INSTANCE;


    }

    private static class DiscoveryThreadHolder {

        private static final DiscoveryThreadClient INSTANCE = new DiscoveryThreadClient();
    }
}
